\documentclass{article}  
\documentclass[12pt,a4paper,openany,fleqn]{book} %声明文档类型

\usepackage{lipsum}
\usepackage[colorlinks]{hyperref}
\renewcommand{\contentsname}{\centerline{目录}}

\usepackage{xeCJK} % 中文
\usepackage{listings} % 插入代码
\usepackage{xcolor} % 代码颜色

%
% 插入代码
%
\lstset{language=C++}%这条命令可以让LaTeX 排版时将C++ 键字突出显示
\lstset{breaklines}%这条命令可以让LaTeX自动将长的代码行换行排版
\lstset{extendedchars=false}% 这一条命令可以解决代码跨页时，章节标题，页眉等汉字不显示的问题
\lstset{
    numbers=left,
    numberstyle= \tiny,
    keywordstyle= \color{ blue!70},
    commentstyle= \color{red!50!green!50!blue!50},
 %   frame=shadowbox, % 阴影效果
    rulesepcolor= \color{ red!20!green!20!blue!20} ,
    escapeinside=``, % 英文分号中可写入中文
    xleftmargin=-2em,xrightmargin=-7em, aboveskip=1em,
    framexleftmargin=2em,
}

\setlength\parskip{\baselineskip}% 增加空行%
\setcounter{tocdepth}{4}%控制目录2 级
\setcounter{secnumdepth}{5}%题号显示层级深度

\begin{document}  

%标题作者
\title{Algorithm}
\author{ShuwenHe\\
何书文\\
Peking University\\
北京大学\\
1201220707@pku.edu.cn}
\date{\today}
\maketitle

\tableofcontents

%
\chapter{Linear List}
\section{基本案例}
\begin{lstlisting}
package main

import(
	"fmt"
)

func main(){
	//map声明
	var a map[string]string
	//在使用map之前，需要先make，make作用就是给map分配数据空间
	//数组声明后，空间就已经分配了
	a = make(map[string]string, 10)
	a["no1"] = "宋江"
	a["no2"] = "吴用"
	a["no1"] = "武松"
	a["no3"] = "吴用"
	fmt.Println(a)
}
\end{lstlisting}
１．ｍap在使用前一定要ｍake\\
2.map的key不能重复,重复以最后一个key-value为准\\
3.map的value是可以相同的\\
4.map的key-value是无序的

\section{map三种使用方式}
\subsection{方式一}
\begin{lstlisting}
//声明，这时map=nil
var cities map[string] string
//分配一个map空间
cities = make(map[string]string, 10)
\end{lstlisting}

\subsection{方式二}
\begin{lstlisting}
//声明就直接make
var cities = make(map[string]string)
\end{lstlisting}

\subsection{方式三}
\begin{lstlisting}
//声明，直接赋值
var cities map[string]string = map[string]string{
  "no4" : "成都",
}
cities["no1"] = "北京"
\end{lstlisting}

\section{基本案例}
\subsection{案例一}
\begin{lstlisting}
package main

import(
	"fmt"
)

func main(){
	//map声明
	var a map[string]string
	//在使用map之前，需要先make，make作用就是给map分配数据空间
	//数组声明后，空间就已经分配了
	a = make(map[string]string, 10)
	a["no1"] = "宋江"
	a["no2"] = "吴用"
	a["no1"] = "武松"
	a["no3"] = "吴用"
	fmt.Println(a)
}

\end{lstlisting}

\subsection{案例二}
\begin{lstlisting}

\end{lstlisting}

%
\chapter{流程控制}

%
\chapter{func}

\section{值传递}
值传递：int,float,bool,string,array,struct\\
基本数据类型和数组默认都是值传递，即值拷贝。在函数内修改，不会影响到原来的值。


\begin{lstlisting}
package main

import (
	"fmt"
)

func test(n1 int){
	n1 += 10
	fmt.Println("test() n1 = ", n1)
}

func  main(){
	n1 := 20
	test(n1)
	fmt.Println("main() n1 = ", n1)
}
运行结果：
test() n1 =  30
main() n1 =  20
\end{lstlisting}

\section{引用传递}
引用传递：point,slice,map,chan,interface\\
函数内部变量修改函数外部变量可以传入变量的地址\&，地址拷贝效率高，struct用\&拷贝效率高

\section{init}
utils/utils.go
\begin{lstlisting}
package utils

import(
	"fmt"
)

var(
	Age int
	Name string
)

func init(){
	fmt.Println("utils包的init()...")
	Age = 120
	Name = "zhangsan"
}
\end{lstlisting}

init.go
\begin{lstlisting}
package main

import (
	"fmt"
	"6.func/utils"
)

var age = test()

// 全局变量先被初始化
func test() int{
	fmt.Println("test()") // 1
	return 90
}

// 在init中完成初始化工作
func init(){
	fmt.Println("init()...") // 2
}

func main(){
	fmt.Println("main()...age = ", age) // 3
	fmt.Println("Age = ", utils.Age,"Name = ", utils.Name) 
}
\end{lstlisting}
运行结果:
\begin{lstlisting}
utils包的init()...
test()
init()...
main()...age =  90
Age =  120 Name =  zhangsan
\end{lstlisting}

%
\section{匿名函数}
定义：没有名字的函数\\
只希望使用一次，可以考虑用匿名函数\\
匿名函数使用方式\\
1.在定义匿名函数时就直接使用\\
2.将匿名函数赋给一个函数变量，再通过该变量来调用匿名函数

\subsection{直接使用}
\begin{lstlisting}
package main 

import(
	"fmt"
)

func main(){

	res := func (n1, n2 int) int {
		return n1 + n2
	}(10, 20)

	fmt.Println("res = ", res)
}
\end{lstlisting}
运行结果：
\begin{lstlisting}
res =  30
\end{lstlisting}

\subsection{函数变量}
main函数里面定义匿名函数
\begin{lstlisting}
package main 

import(
	"fmt"
)

func main(){

	// 将匿名函数func (n1, n2 int) int 赋给res变量
	// 则res的数据类型就是函数类型，通过res调用
	res := func (n1, n2 int) int {
		return n1 - n2
	}

	res1 := res(2, 1)
	fmt.Println("res = ", res1)
}
\end{lstlisting}
运行结果：
\begin{lstlisting}
res =  1
\end{lstlisting}

%
\subsection{全局匿名函数}
定义：匿名函数赋给一个全局变量，可以在整个程序中有效
\begin{lstlisting}
package main 

import(
	"fmt"
)

var (
	// Fun1全局匿名函数
	res = func (n1, n2 int) int{
		return n1 * n2
	}
)

func main(){

	res1 := res(2, 3)
	fmt.Println("res = ", res1)
}
\end{lstlisting}
运行结果：
\begin{lstlisting}
res =  6
\end{lstlisting}

%
\section{闭包}
定义：一个函数和与其相关的引用环境组合的一个整体

\begin{lstlisting}
// 闭包是返回的匿名函数和这个匿名函数用到外部的变量共同构成闭包,变量不是每一次都被初始化
package main

import (
	"fmt"
)

// 累加器
// add_upper是一个函数，返回类型是 func (int) int
func add_upper() func (int) int{
	// 下面是闭包，返回的是匿名函数，这个匿名函数引用到函数外的n
	// 因此这个匿名函数与n就形成一个整体，构成闭包
	// 闭包是一个类,函数是操作，n是字段
	// 函数和它使用到的n构成一个闭包
	// 当反复调用f函数时，因为n初始化一次，每调用一次就进行累加
	// 返回的函数使用到哪些变量，函数和它引用到的变量共同构成闭包
	var n int = 10
	var str = "hello"
	return func (x int) int {
		n = n + x
		str += string(36)
		fmt.Println("str = ", str)
		return n
	}
}

func main(){
	// 使用前面的代码
	f := add_upper()
	fmt.Println(f(1))
	fmt.Println(f(2))
	fmt.Println(f(3))
}
\end{lstlisting}

运行结果：
\begin{lstlisting}
str =  hello$
11
str =  hello$
13
str =  hello$$$
16
\end{lstlisting}

%
\chapter{struct}
\section{struct}
1)结构体是自定义的数据类型，代表一类事物\\
结构体是值类型
\begin{lstlisting}
type user struct{
  name string
  age int
  sex string
}

var bill user	
\end{lstlisting}

\begin{lstlisting}
package main

import (
	"fmt"
)


type Cat struct{
		Name string
		Age int
		Color string
		Hobby string
	}

func main(){

	var cat1 Cat
	cat1.Name = "小白"
	cat1.Age = 3
	cat1.Color = "白色"
	cat1.Hobby = "吃鱼"
	fmt.Println("cat1 = ", cat1)
	//
	fmt.Println("猫猫信息:")
	fmt.Println("name", cat1.Name)
	fmt.Println("Age", cat1.Age)
	fmt.Println("color", cat1.Color)
	fmt.Println("hobby", cat1.Hobby)
}
\end{lstlisting}

\section{结构体变量实例在内存中布局}
结构体变量实例在内存中布局

\section{结构体是值类型}
go结构体是值类型

%
\chapter{method}
\section{增加参数}
方法函数区别：func方法名之间增加一个参数\\
\begin{lstlisting}
type user struct {
  name string
  email string
}

func (u *user) changeEmail(email string){
  u.email = email
}

func main(){
  bill := &user("bill", "bill@email.com")
  bill.notify()
}
\end{lstlisting}

%
\chapter{反射}

%
\chapter{goroutine}
\section{go协程特点}
1.有独立的栈空间
\begin{lstlisting}
package main

import(
	"fmt"
)

func sum(n1 int, n2 int) int{
	defer fmt.Println("n1 = 先入栈，后出栈", n1)
	defer fmt.Println("n2 = 后入栈，先出栈", n2)
	n1++
	n2++

	res := n1 + n2 // 30
	fmt.Println("先输出defer后面res=",res)
	return res
}

func main(){
	res := sum(10, 20)
	fmt.Println("res = ", res)
}
\end{lstlisting}
以主线程退出为主

%
\chapter{channel}

%
\chapter{文件读取}

%
\chapter{时间和日期}

%
\chapter{string}

%
\chapter{正则处理}

%
\chapter{锁sync}

%
\chapter{net}

%
\chapter{GPM模型}
\section{G}
\section{P}
\section{M}

%
\chapter{point}

%
\chapter{内存布局}

%
\chapter{内存管理}

%
\chapter{GC}

%
\chapter{CGO}

%
\chapter{goroutine调度}

%
\chapter{channel调度}

%
\chapter{转义字符}
%
\section{制表符}
\begin{lstlisting}
package main

import (
	"fmt"
)

func main(){
	fmt.Println("zhangsan\tlisi")
}
\end{lstlisting}

%
\section{换行符}
\begin{lstlisting}
package main

import (
	"fmt"
)

func main(){
	fmt.Println("zhangsan\nlisi")
}
\end{lstlisting}

%
\section{反斜杠}
\begin{lstlisting}
package main

import (
	"fmt"
)

func main(){
	fmt.Println("c:\\users\\Administrator\\Desktop\\test.txt")
}
\end{lstlisting}

%
\section{双引号}
\begin{lstlisting}
package main

import (
	"fmt"
)

func main(){
	fmt.Println("zhangsan say:\"hello, world!\"")
}
\end{lstlisting}

%
\section{替换}
\begin{lstlisting}
package main

import (
	"fmt"
)

func main(){
	fmt.Println("abc\r12")
}
\end{lstlisting}

%
\chapter{defer}
\section{FILO}
\begin{lstlisting}
package main

import(
	"fmt"
)

func sum(n1 int, n2 int) int{
	defer fmt.Println("n1 = 先入栈，后出栈", n1)
	defer fmt.Println("n2 = 后入栈，先出栈", n2)

	res := n1 + n2 // 30
	fmt.Println("先输出defer后面res=",res)
	return res
}

func main(){
	res := sum(10, 20)
	fmt.Println("res = ", res)
}
\end{lstlisting}

\section{file}
%
\begin{lstlisting}
func test(){
  file = openfile(file_name)
  defer file.close()
}
\end{lstlisting}

\section{mysql}
%
\begin{lstlisting}
func test(){
  conn = open_database()
  defer conn.close()
}
\end{lstlisting}

%
\chapter{面向对象}
go基于struct实现oop特性\\
把猫的特征提取出来->cat结构体->变量(实例)\\
把人的特征提取出来->person结构体->变量(实例)

\section{继承}
go没有extends关键字，继承通过匿名字段实现

%
\chapter{rand}
\section{Intn}
%
\begin{lstlisting}
package main 

import (
	"fmt"
	"math/rand"
	"time"
)

func main(){
	// 为了生成一个随机数，还要给rand设置种子
	// time.Now().Unix：返回一个Unix将t表示为Unix时间，即从时间点January 1, 1970 UTC到时间点t所经过的时间
	//fmt.Println(time.Now().Unix())
	// 随机生成1-100整数
	var count int = 0
	for{
		rand.Seed(time.Now().UnixNano())
		n := rand.Intn(100) + 1
		fmt.Println("n = ", n)
		count++
		if (n== 99) {
			break
		}
	}
	fmt.Println("生成99共使用:",count)
}
\end{lstlisting}

%
\chapter{break}
break:跳转控制语句
\section{for循环使用break}
当执行到break,就直接终止for循环，跳出for循环
%
\begin{lstlisting}

\end{lstlisting}

%
\chapter{switch}
1.switch基于不同条件执行不同动作，每个case分支都是唯一的，从上到下逐一测试，直到匹配\\
2.匹配后面不需要加break
\section{基本语法}
3.go中case语句块不用break，默认会有，程序执行完\\
4.
%
\begin{lstlisting}
switch 表达式{
case 表达式1，表达式2，...:
  语句块1
case 表达式3，表达式4，...:
  语句块2
default: // 一个没有匹配，执行默认语句
  语句块
}
\end{lstlisting}

%
\section{switch快速入门案例}
星期


\begin{lstlisting}
package main

import (
	"fmt"
)

func test(char byte) byte{
	return char + 1
}

func main(){
	// 星期
	var key byte
	fmt.Println("请输入abcdefg")
	fmt.Scanf("%c", &key)
    // switch后面数据类型要于case后面的数据类型一致 
	switch test(key) + 1{
	case 'a': fmt.Println("周一")
	case 'b': fmt.Println("周二")
	case 'c':
		fmt.Println("周三")
	default:
		fmt.Println("输入有误")
	}
}
\end{lstlisting}

%
\chapter{tcp/ip}

%
\section{server Listen监听}
Listen函数创建的服务端：
\begin{lstlisting}
package main

import(
	"fmt"
	"net" // 做网络socket开发时，net包含有我们需要所有的方法和函数
	"io"
)

// process要拿到这个连接
func process(conn net.Conn){
	// 循环接收客户端发送的数据
	defer conn.Close() //关闭conn,连接不释放满了别的不能连接
	for{
		// 创建切片
		buf := make([]byte, 1024)
		// 1.等待客户端通过conn发送信息
		// 2.如果客户端没有write(发送)，那么goroutine就阻塞在这里
		fmt.Printf("服务器在等待client%s发送信息\n", conn.RemoteAddr().String())
		n, err := conn.Read(buf) // 从conn读取
		if err == io.EOF{
			fmt.Print("客户端退出")
			return
		}
		// 3.显示客户端发送的内容到服务器的终端
		fmt.Print(string(buf[:n]))
	}
}

func main(){
	fmt.Println("服务器开始监听...")
	ln, err := net.Listen("tcp", "192.168.1.104:8080") // net.Listen监听

	if err != nil {
		fmt.Println("listen err", err)
		return
	}

	defer ln.Close() // 延时关闭

	for {
		// 等待客户端连接
		fmt.Println("等待客户端连接...")
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println("Accept() err", err)
		}else{
//			fmt.Printf("Accept() suc conn = %v客户端ip = %v\n", conn, conn.RemoteAddr().String())
		}
		go process(conn)
	}
	// 启动一个协程，为客户端服务

	//fmt.Printf("listen=%v\n", ln)

}
\end{lstlisting}

%
\section{client Dial拨号}
Dial函数和服务端建立连接：\\
IP+端口
\begin{lstlisting}
package main

import(
	"fmt"
	"net"
	"bufio"
	"os"
)

func main(){
	conn, err := net.Dial("tcp", "192.168.1.104:8080") // Dial拨号
	if err != nil{
		fmt.Println("client dial err = ", err)
		return
	}
	// 功能一：客户端发送单行数据,然后退出
	reader := bufio.NewReader(os.Stdin) // os.Stdin代表标准输入[终端]
	// 从终端读取一行用户输入，并准备发送给服务器
	line, err := reader.ReadString('\n')
	if err != nil{
		fmt.Println("ReadString err = ", err)
	}
	// 
	n, err := conn.Write([]byte(line))
	if err != nil{
		fmt.Println("conn.Write err = ", err)
	}
	fmt.Printf("客户端送%d字节数据，并退出", n)

}
\end{lstlisting}

%
\section{type Conn interface}
Conn接口代表通用的面向流的网络连接。多个线程可能会同时调用同一个Conn的方法。
\begin{lstlisting}
type Conn interface {
    // Read从连接中读取数据
    // Read方法可能会在超过某个固定时间限制后超时返回错误，该错误的Timeout()方法返回真
    Read(b []byte) (n int, err error)
    // Write从连接中写入数据
    // Write方法可能会在超过某个固定时间限制后超时返回错误，该错误的Timeout()方法返回真
    Write(b []byte) (n int, err error)
    // Close方法关闭该连接
    // 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
    Close() error
    // 返回本地网络地址
    LocalAddr() Addr
    // 返回远端网络地址
    RemoteAddr() Addr
    // 设定该连接的读写deadline，等价于同时调用SetReadDeadline和SetWriteDeadline
    // deadline是一个绝对时间，超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
    // deadline对之后的所有I/O操作都起效，而不仅仅是下一次的读或写操作
    // 参数t为零值表示不设置期限
    SetDeadline(t time.Time) error
    // 设定该连接的读操作deadline，参数t为零值表示不设置期限
    SetReadDeadline(t time.Time) error
    // 设定该连接的写操作deadline，参数t为零值表示不设置期限
    // 即使写入超时，返回值n也可能>0，说明成功写入了部分数据
    SetWriteDeadline(t time.Time) error
}
\end{lstlisting}

%
\section{网络编程2种}
1.tcp socket编程\\
2.b/s结构http编程

%
\section{分层}
1.应用层application\\
2.传输层tcp\\
3.ip层\\
4.链路层

\section{ip}
1.每个internet上的主机和路由器都有一个ip地址，包括网络号和主机号\\
2.ip地址有ipv4(32位)或ipv6(128位),ifconfig查看

%
\section{监听port}
1.只要是做服务程序，都必须监听一个端口\\
2.该端口就是其他程序和该服务通讯的通道\\
3.一台电脑有65535个端口\\
4.一旦一个端口被某个程序监听(占用)，那么其他的程序就不能在该端口监听

%
\section{port分类}
\subsection{查看port}
netstat -an

\subsection{保留port}
0号是保留端口

\subsection{固定port}
固定端口：1-1024(有名端口)\\
被某些程序固定使用，一般程序员不使用\\
22SSH远程登录协议\\
23telnet\\
21ftp\\
25smtp\\
80iis\\
7echo

\subsection{动态port}
动态端口1025-65535程序员可以使用

\subsection{port注意}
1.服务器尽量少开端口\\
2.一个端口只能被一个程序监听\\
3.netstat -an本机有哪些端口在监听

%
\section{tcp socket编程}
\subsection{server}
1.监听port  8888\\
2.接收client的tcp连接，建立client和server端连接\\
3.创建goroutine，处理连接请求，客户端通过连接发送请求包

\subsection{client}
1.建立与服务端的连接\\
2.发送请求数据，接收服务器端返回的结果数据\\
3.关闭连接

%
\chapter{redis}
\section{•}
http://www.runoob.com/redis/lists-lpush.html
\section{lpush}
\begin{lstlisting}
lpush city beijing shanghai tianjin
\end{lstlisting}

\section{lpush}
\begin{lstlisting}
lrange city 0 -1
\end{lstlisting}

%
\chapter{abstract}
\section{}
\subsection{}
\begin{lstlisting}
package main

import(
	"fmt"
)

// 定义一个结构体
type Account struct{
	AccountNo string
	Pwd string
	Balance float64
}

//方法
//1.存款
func (account *Account)Deposite(money float64, pwd string){
	// 输入密码是否正确
	if pwd != account.Pwd{
		fmt.Println("输入密码不正确")
		return 
	}
	// 看看存款金额
	if money <= 0{
		fmt.Println("输入金额不正确")
		return
	}
	account.Balance += money
	fmt.Println("存款成功")
}


//方法
//1.存款
func (account *Account)WithDraw(money float64, pwd string){
	// 输入密码是否正确
	if pwd != account.Pwd{
		fmt.Println("输入密码不正确")
		return 
	}
	// 看看取款金额
	if money <= 0 || money > account.Balance{
		fmt.Println("输入金额不正确")
		return
	}
	account.Balance -= money
	fmt.Println("存款成功")
}


// 查询余额
func (account *Account)Query(pwd string){
	// 输入密码是否正确
	if pwd != account.Pwd{
		fmt.Println("输入密码不正确")
		return 
	}
	fmt.Printf("你的账号为=%v余额=%v\n",account.AccountNo, account.Balance)
}

func main(){
	//测试
	account := Account{
		AccountNo : "zhangsan123",
		Pwd : "123456",
		Balance : 100.0,
	}
	account.Query("123456")
	account.Deposite(200.0, "123456")
	account.Query("123456")
	account.WithDraw(150.0, "123456")
	account.Query("123456")
}
\end{lstlisting}

%
\chapter{封装encapsulation}
\subsection{封装介绍}
封装就是把抽象出的字段和对字段的操作封装在一起，数据被保护在内部，程序的其他包只有通过被授权的操作(方法)，才能对字段进行操作。
\subsection{对电视机操作}
遥控器对电视机的操作就是封装，方法里面就是封装
\subsection{封装的理解和好处}
1.隐藏实现细节\\
2.可以对数据进行验证，保证安全合理

\subsection{如何体现封装}
1.对结构体中的属性进行封装\\
2.通过方法，包实现封装

\subsection{封装的实现步骤}
1.将结构体、字段(属性)的首字母小写(不能到处了，其他包不能使用，类似private)\\
2.给结构体所在的包提供一个工厂模式的函数，首字母大写，类似一个构造函数\\
3.提供一个首字母大写的Set方法(类似其他语言的public)，用于对属性判断并赋值
\begin{lstlisting}
func (var 结构体类型名) Setxxx(参数列表)(返回值列表){
  // 加入数据验证的业务逻辑
  // var字段 = 参数
}
\end{lstlisting}
4.提供一个首字母大写的Get方法(类似其他语言的public),用于获取属性的值
\begin{lstlisting}
func (var 结构体类型名) Getxxx(参数列表)(返回值列表){
  // 加入数据验证的业务逻辑
  // var字段 = 参数
}
\end{lstlisting}

\subsection{}
\begin{lstlisting}

\end{lstlisting}

%
\chapter{继承extends}
\section{匿名结构体}
\subsection{}
1.共有字段或方法\\
2.匿名结构体，实现继承特性

\subsection{}
\begin{lstlisting}
package main

import(
	"fmt"
)

type A struct{
	Name string
	age int
}

type B struct{
	Name string
	Score float64
}

type C struct{
	A
	B
	//Name string
}

type D struct{
	a A // 有名结构体 
}

type Goods struct{
	Name string
	Price float64
}

type Brand struct{
	Name string
	Address string
}

type TV struct{
	Goods
	Brand
}

type TV2 struct{
	*Goods
	*Brand
}

type Monster struct{
	Name string
	Age int
}

type E struct{
	Monster
	int
	n int
}

func main(){
	var c C
	//如果c没有Name字段，而A和B有Name字段，这是必须通过指定匿名结构体名字来区分
	//c.Name = "tom" //选择器模糊错误ambiguous selector c.Name
	c.A.Name = "tom"
	fmt.Println("c")

	var d D
	d.a.Name = "jack"

	tv := TV{Goods{"电视机001",5000.99}, Brand{"海尔","山东"},}

	tv2 := TV{
		Goods{
			Price : 5000.99,
			Name : "电视机002",
		},
		Brand{
			Name : "夏普",
			Address : "北京",
		},
	}

	fmt.Println("tv", tv)
	fmt.Println("tv2", tv2)

	tv3 := TV2{&Goods{"电视机003",7000.99}, &Brand{"创维","河南"},}
	tv4 := TV2{
		&Goods{
			Name : "电视机004",
			Price : 9000.99,
		}, 
		&Brand{
			Name : "长虹",
			Address : "四川",
		},
	}

	fmt.Println("tv3", *tv3.Goods, *tv3.Brand)
	fmt.Println("tv4", *tv4.Goods, *tv4.Brand)

	//匿名字段
	var e E
	e.Name = "狐狸精"
	e.Age = 300
	e.int = 20
	e.n = 40
	fmt.Println("e", e)
}
\end{lstlisting}

\section{多重继承}
\subsection{多重继承细节说明}
1.嵌入的匿名结构体有相同的字段名或者方法名，则在访问时，需要通过匿名结构体类型名来区分

%
\chapter{interface}
\section{基本介绍}
\subsection{为什么要接口}
1.减少耦合，松耦合\\
2.通用性\\
3.usb接口\\
4.interface类型可以定义一组方法，但是这些不需要实现\\
5.interface不能包含任何变量\\
6.某个自定义类型(比如结构体Phone)使用时，根据具体情况把这些方法写出来\\
7.接口里的说有方法都没有方法体，即接口的方法都是没有实现的方法\\
8.接口体现：多态和高内聚低耦合\\
9.高内聚:把所有代码写到函数里面\\
10.低耦合：程序之间耦合性降低了

%
\chapter{json}
\section{验证网址}
https://www.json.cn/
\section{}
网络传输先将数据(结构体，map等)序列化成json字符串，到接收方得到json字符串时，再反序列化恢复成原来的数据类型(结构体，map等)

\section{}
1.任何数据类型都可以转成json格式
\begin{lstlisting}
[{“key1”:val1, "key2":val2, "key3":val3,"key4":[val4, val5]},
{“key1”:val1, "key2":val2, "key3":val3,"key4":[val4, val5]},]
\end{lstlisting}

\section{}
\begin{lstlisting}
[{
"name":"tom",
"age":20,
"address":[
"北京",
"上海"],
"hobby":"pinping"
},
{
"name":"mary",
"age":18,
"address":[
"天津",
"成都"]
}
]
\end{lstlisting}

\section{json序列化}
json序列化是指:将有key value结构的数据类型(比如结构体、map、slice)序列化成json字符串的操作。
\begin{lstlisting}
package main

import(
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct{
	Name string
	Age int 
	Birthday string
	Sal float64
	Skill string
}

func teststruct(){
	//
	monster := Monster{
		Name : "牛魔王",
		Age : 500,
		Birthday : "2011-11-11",
		Sal : 8000.00,
		Skill : "牛魔拳",
	}
	//将monster序列化
	data, err := json.Marshal(&monster)
	if err != nil{
		fmt.Printf("序列化错误 err = %v\n",err)
	}
	// 输出序列化后的错误
	fmt.Printf("struct序列化后=%v\n", string(data))
}

//将map进行序列化
func testMap(){
	// 定义一个map
	var a map[string]interface{}
	//使用map之前需要make
	a = make(map[string]interface{})
	a["name"] = "红孩儿"
	a["age"] = 30
	a["address"] = "火云洞"

	//将a这个map进行序列化
	data, err := json.Marshal(a)
	if err != nil{
		fmt.Printf("序列化错误 err = %v\n",err)
	}
	// 输出序列化后的错误
	fmt.Printf("map序列化后=%v\n", string(data))
}

//对切片进行序列化
func test_slice(){
	var slice []map[string]interface{}

	var m1 map[string]interface{}
	//使用map前，需要先make
	m1 = make(map[string]interface{})
	m1["name"] = "jack"
	m1["age"] = "7"
	m1["address"] = "北京"
	slice = append(slice, m1)
	
	var m2 map[string]interface{}
	//使用map前，需要先make
	m2 = make(map[string]interface{})
	m2["name"] = "tom"
	m2["age"] = "20"
	m2["address"] = [2]string{"墨西哥","夏威夷"}
	slice = append(slice, m2)

	//将a这个map进行序列化
	data, err := json.Marshal(slice)
	if err != nil{
		fmt.Printf("序列化错误 err = %v\n",err)
	}
	// 输出序列化后的错误
	fmt.Printf("slice序列化后=%v\n", string(data))
}

//对基本数据类型序列化,对基本数据类型序列化意义不大
func test_float64(){
	var num1 float64 = 2345.67
	//将a这个map进行序列化
	data, err := json.Marshal(num1)
	if err != nil{
		fmt.Printf("序列化错误 err = %v\n",err)
	}
	// 输出序列化后的错误
	fmt.Printf("float64序列化后=%v\n", string(data))
}

func main(){
	//将struct,map,slice序列化
	teststruct()
	testMap()
	test_slice()
	test_float64()
}
\end{lstlisting}

\section{json反序列化}
\begin{lstlisting}

\end{lstlisting}

%
\chapter{sync}

%
\section{waitgroup.go}
A WaitGroup waits for a collection of goroutines to finish.


%
\chapter{context}

%
\chapter{beego}
\section{•}

%
\chapter{new函数}
\section{创建变量的方式}
使用内置的new函数

%
\chapter{堆栈分配空间}
\section{函数内堆栈}
1.global = \&x (x使用堆空间)\\
2.*y = 1 （y使用栈空间）

%
\chapter{RPC}
远程过程调用（Remote Procedure Call，缩写为 RPC）是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序，而程序员无需额外地为这个交互作用编程。 如果涉及的软件采用面向对象编程，那么远程过程调用亦可称作远程调用或远程方法调用。维基百科：远程过程调用

用通俗易懂的语言描述就是：RPC允许跨机器、跨语言调用计算机程序方法。打个比方，我用go语言写了个获取用户信息的方法getUserInfo，并把go程序部署在阿里云服务器上面，现在我有一个部署在腾讯云上面的php项目，需要调用golang的getUserInfo方法获取用户信息，php跨机器调用go方法的过程就是RPC调用。

Go官方提供了一个RPC库: net/rpc。包rpc提供了通过网络访问一个对象的方法的能力。服务器需要注册对象， 通过对象的类型名暴露这个服务。注册后这个对象的输出方法就可以远程调用，这个库封装了底层传输的细节，包括序列化。服务器可以注册多个不同类型的对象，但是注册相同类型的多个对象的时候回出错。

高性能的分布式全功能的RPC框架	rpcx






\end{document}  
